home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Peter Lewis / FetchNews 1.0.0b / source / FetchNews.p < prev    next >
Encoding:
Text File  |  1993-03-12  |  10.6 KB  |  453 lines  |  [TEXT/PJMM]

  1. program FetchNews;
  2.  
  3.     uses
  4.         TCPStuff, TCPConnections, MyTypes, MyUtils, MyBufferedTCP, MyHandleFile, MyFileSystemUtils;
  5.  
  6.     const
  7.         bad_rn = -32768;
  8.         nntp_port = 119;
  9.         text_creator = 'ttxt';
  10.         global_strh_id = 200;
  11.         data_filename_index = 1;
  12.         demo_name_index = 2;
  13.         active_index = 3;
  14.         initial_command_index = 4;
  15.         data_strh_id = 128;
  16.         host_index = 1;
  17.         groups_index = 2;
  18.  
  19.     var
  20.         error: OSErr;
  21.         quitNow: boolean;
  22.         buffer: TCPBuffer;
  23.         state: (S_None, S_WaitingForHello, S_WaitingOnInitialCommand, S_GettingList, {}
  24.             S_WaitingForGroupReply, S_WaitingForArticleReply, S_WaitingForDot, S_Quiting);
  25.         time_of_last_action: longInt;
  26.         nntp_connection: connectionIndex;
  27.         nntp_tcpc: TCPConnectionPtr;
  28.         list_hf: HandleFile;
  29.         demo_vrn: integer;
  30.         demo_dirID: longInt;
  31.         transfering: boolean;
  32.         transfering_refnum: integer;
  33.         article_dirID: longInt;
  34.         article_startID, article_curID, article_endID: longInt;
  35.  
  36.     function GetIndStr (id, index: integer): str255;
  37.         var
  38.             s: str255;
  39.     begin
  40.         GetIndString(s, id, index);
  41.         GetIndStr := s;
  42.     end;
  43.  
  44.     function GetGlobalStr (index: integer): str255;
  45.     begin
  46.         GetGlobalStr := GetIndStr(global_strh_id, index);
  47.     end;
  48.  
  49.     procedure FailError (oe: OSErr);
  50.     begin
  51.         if error = noErr then
  52.             error := oe;
  53.     end;
  54.  
  55.     function NoError (oe: OSErr): boolean;
  56.     begin
  57.         if error = noErr then
  58.             error := oe;
  59.         NoError := error = noErr;
  60.     end;
  61.  
  62.     function MakeDirectory (name: str255; parID: longInt; var dirID: longInt): OSErr;
  63.         var
  64.             pb: CInfoPBRec;
  65.             oe: OSErr;
  66.             dummy: longInt;
  67.     begin
  68.         oe := DirCreate(demo_vrn, parID, name, dummy);
  69.         MakeDirectory := MyGetCatInfo(demo_vrn, parID, name, 0, pb);
  70.         dirID := pb.ioDirID;
  71.     end;
  72.  
  73.     procedure SendLine (s: str255);
  74.         var
  75.             oe: OSErr;
  76.     begin
  77. {    writeln('>', s);}
  78.         s := concat(s, cr, lf);
  79.         oe := TCPSendAsync(nntp_tcpc, @s[1], length(s), true, nil);
  80.     end;
  81.  
  82.     procedure GetWordDel (var line, word: str255; del: char);
  83.         var
  84.             p: integer;
  85.     begin
  86.         p := Pos(del, line);
  87.         if p > 0 then begin
  88.             word := copy(line, 1, p - 1);
  89.             line := copy(line, p + 1, 255);
  90.         end
  91.         else begin
  92.             word := line;
  93.             line := '';
  94.         end;
  95.     end;
  96.  
  97.     procedure GetWord (var line, word: str255);
  98.     begin
  99.         GetWordDel(line, word, ' ');
  100.     end;
  101.  
  102.     function FirstWord (line: str255): str255;
  103.         var
  104.             word: str255;
  105.     begin
  106.         GetWord(line, word);
  107.         FirstWord := word;
  108.     end;
  109.  
  110.     function Match (s, pattern: str255): boolean;
  111.     begin
  112.         if pattern[length(pattern)] = '*' then begin
  113.             Match := IUEqualString(copy(s, 1, length(pattern) - 1), copy(pattern, 1, length(pattern) - 1)) = 0;
  114.         end
  115.         else begin
  116.             Match := IUEqualString(s, pattern) = 0;
  117.         end;
  118.     end;
  119.  
  120.     function GoodGroup (s: str255): boolean;
  121.         var
  122.             g: str255;
  123.             i: integer;
  124.     begin
  125.         GoodGroup := false;
  126.         i := groups_index;
  127.         g := GetIndStr(data_strh_id, i);
  128.         while g <> '' do begin
  129.             if Match(s, g) then begin
  130.                 GoodGroup := true;
  131.                 leave;
  132.             end;
  133.             i := i + 1;
  134.             g := GetIndStr(data_strh_id, i);
  135.         end;
  136.     end;
  137.  
  138.     function WriteHandleToFile (var fs: FSSpec; h: handle; fcreator, ftype: OSType): OSErr;
  139.         var
  140.             oe, ooe: OSErr;
  141.             count: longInt;
  142.             refnum: integer;
  143.     begin
  144.         ooe := HCreate(fs.vRefNum, fs.parID, fs.name, fcreator, ftype);
  145.         oe := HOpen(fs.vRefNum, fs.parID, fs.name, fsRdWrPerm, refnum);
  146.         if oe = noErr then begin
  147.             ooe := SetEOF(refnum, 0);
  148.             count := GetHandleSize(h);
  149.             oe := FSWrite(refnum, count, h^); { Anyone with sufficent paranoia would lock this handle! }
  150.             if (oe = noErr) and (count <> GetHandleSize(h)) then
  151.                 oe := eofErr;
  152.             ooe := FSClose(refnum);
  153.         end;
  154.         WriteHandleToFile := oe;
  155.     end;
  156.  
  157.     procedure CleanOutDirectory;
  158.         var
  159.             index, i: integer;
  160.             oe, ooe: OSErr;
  161.             pb: CInfoPBRec;
  162.             name: str255;
  163.             delete_it: boolean;
  164.             n: longInt;
  165.     begin
  166.         index := 1;
  167.         repeat
  168.             oe := MyGetCatInfo(demo_vrn, article_dirID, name, index, pb);
  169.             if oe = noErr then begin
  170.                 delete_it := true;
  171.                 for i := 1 to length(name) do begin
  172.                     if (name[i] < '0') or (name[i] > '9') then begin
  173.                         delete_it := false;
  174.                         leave;
  175.                     end;
  176.                 end;
  177.                 if delete_it then begin
  178.                     n := StrToNum(name);
  179.                     delete_it := (n < article_startID) | (n > article_endID);
  180.                 end;
  181.                 if delete_it then begin
  182.                     ooe := HDelete(demo_vrn, article_dirID, name);
  183.                     delete_it := ooe = noErr;
  184.                 end;
  185.                 if not delete_it then
  186.                     index := index + 1;
  187.             end;
  188.         until (oe <> noErr);
  189.     end;
  190.  
  191.     procedure HandleLine (line: str255);
  192.         procedure DoQuit;
  193.         begin
  194.             SendLine('QUIT');
  195.             state := S_Quiting;
  196.         end;
  197.  
  198.         procedure StartGroup;
  199.             var
  200.                 line, group, dir, s: str255;
  201.         begin
  202.             if not ReadFromHandleFile(list_hf, line) | (line = '.') then begin
  203.                 DoQuit;
  204.             end
  205.             else begin
  206.                 GetWord(line, group);
  207.                 GetWord(line, s);
  208.                 article_endID := StrToNum(s);
  209.                 GetWord(line, s);
  210.                 article_startID := StrToNum(s);
  211.                 article_curID := article_startID;
  212.                 article_dirID := demo_dirID;
  213.                 s := group;
  214.                 while (s <> '') & (error = noErr) do begin
  215.                     GetWordDel(s, dir, '.');
  216.                     FailError(MakeDirectory(dir, article_dirID, article_dirID));
  217.                 end;
  218.                 if error = noErr then begin
  219.                     CleanOutDirectory;
  220.                     SendLine(concat('GROUP ', group));
  221.                     state := S_WaitingForGroupReply;
  222.                 end;
  223.             end;
  224.         end;
  225.  
  226.         procedure StartArticle;
  227.             var
  228.                 name: str63;
  229.                 fi: FInfo;
  230.                 started: boolean;
  231.         begin
  232.             started := false;
  233.             while not started and (article_curID <= article_endID) do begin
  234.                 if HGetFInfo(demo_vrn, article_dirID, NumToStr(article_curID), fi) = noErr then begin
  235.                     article_curID := article_curID + 1;
  236.                 end
  237.                 else begin
  238.                     SendLine(concat('ARTICLE ', NumToStr(article_curID)));
  239.                     state := S_WaitingForArticleReply;
  240.                     started := true;
  241.                 end;
  242.             end;
  243.             if not started then
  244.                 StartGroup;
  245.         end;
  246.  
  247.         var
  248.             active_fs: FSSpec;
  249.             oe: OSErr;
  250.     begin
  251.         case state of
  252.             S_WaitingForHello:  begin
  253.                 if line[1] = '2' then begin
  254.                     SendLine(GetGlobalStr(initial_command_index));
  255.                     state := S_WaitingOnInitialCommand;
  256.                 end
  257.                 else begin
  258.                     FailError(-3);
  259.                     DoQuit;
  260.                 end;
  261.             end;
  262.             S_WaitingOnInitialCommand:  begin
  263.                 SendLine('LIST');
  264.                 state := S_GettingList;
  265.             end;
  266.             S_GettingList:  begin
  267.                 if line = '.' then begin
  268.                     WriteToHandleFile(list_hf, line);
  269.                     oe := MyFSMakeFSSpec(demo_vrn, demo_dirID, GetGlobalStr(active_index), active_fs);
  270.                     if oe = fnfErr then
  271.                         oe := noErr;
  272.                     if NoError(oe) then begin
  273.                         if NoError(WriteHandleToFile(active_fs, list_hf.data, text_creator, 'TEXT')) then begin
  274.                             list_hf.pos := 0;
  275.                             StartGroup;
  276.                         end;
  277.                     end;
  278.                     if error <> noErr then
  279.                         DoQuit;
  280.                 end
  281.                 else if GoodGroup(FirstWord(line)) then begin
  282.                     WriteToHandleFile(list_hf, line);
  283.                 end;
  284.             end;
  285.             S_WaitingForGroupReply:  begin
  286.                 if line[1] = '2' then begin
  287.                     StartArticle;
  288.                 end
  289.                 else begin
  290.                     StartGroup;
  291.                 end;
  292.             end;
  293.             S_WaitingForArticleReply:  begin
  294.                 if line[1] = '2' then begin
  295.                     oe := HCreate(demo_vrn, article_dirID, NumToStr(article_curID), text_creator, 'TEXT');
  296.                     oe := HOpen(demo_vrn, article_dirID, NumToStr(article_curID), fsRdWrPerm, transfering_refnum);
  297.                     if oe <> noErr then begin
  298.                         transfering_refnum := bad_rn; { best we can do, oh well }
  299.                         FailError(oe);
  300.                     end;
  301.                     transfering := true;
  302.                     state := S_WaitingForDot;
  303.                 end
  304.                 else begin
  305.                     article_curID := article_curID + 1;
  306.                     StartArticle;
  307.                 end;
  308.             end;
  309.             S_WaitingForDot:  begin
  310.                 oe := FSClose(transfering_refnum);
  311.                 transfering := false;
  312.                 article_curID := article_curID + 1;
  313.                 StartArticle;
  314.             end;
  315.             S_Quiting:  begin
  316.                 state := S_None;
  317.                 CloseConnection(nntp_connection);
  318.             end;
  319.             otherwise
  320.                 ;
  321.         end;
  322.     end;
  323.  
  324.     procedure WNE;
  325.         var
  326.             dummy: boolean;
  327.             er: eventRecord;
  328.     begin
  329.         dummy := WaitNextEvent(everyEvent, er, 5, nil);
  330.     end;
  331.  
  332.     procedure HCE;
  333.         var
  334.             cer: connectionEventRecord;
  335.             line: str255;
  336.             finished: boolean;
  337.     begin
  338.         if GetConnectionEvent(any_connection, cer) then begin
  339.             with cer do begin
  340.                 case event of
  341.                     C_Found:  begin
  342.                         if not NoError(NewActiveConnection(nntp_connection, Default_TCPBUFFERSIZE, value, nntp_port, nil)) then
  343.                             quitNow := true;
  344.                     end;
  345.                     C_SearchFailed:  begin
  346.                         FailError(-2);
  347.                         quitNow := true;
  348.                     end;
  349.                     C_Established:  begin
  350.                         nntp_tcpc := cer.tcpc;
  351.                         time_of_last_action := TickCount;
  352.                         SetHeartBeat(connection, 30 * 60); { Heart beat every 30 seconds }
  353.                         if not NoError(TBCreate(buffer, tcpc, 10000)) then begin
  354.                             quitNow := true;
  355.                             CloseConnection(connection);
  356.                         end
  357.                         else begin
  358.                             state := S_WaitingForHello;
  359.                         end;
  360.                     end;
  361.                     C_HeartBeat:  begin
  362.                         if (TickCount - time_of_last_action) >= longInt(1) * 60 * 60 then begin
  363.                             FailError(-4);
  364.                             SetHeartBeat(connection, -1);
  365.                             CloseConnection(connection);
  366.                             quitNow := true;
  367.                         end;
  368.                     end;
  369.                     C_CharsAvailable:  begin
  370.                         TBReadChars(buffer, value);
  371.                         while not transfering and TBGetLine(buffer, line) do begin
  372. {    if (copy(line, 1, 3) <> 'mis') & (copy(line, 1, 3) <> 'sci') & (copy(line, 1, 3) <> 'alt') & (copy(line, 1, 3) <> 'com') & (copy(line, 1, 3) <> 'bit') & (copy(line, 1, 3) <> 'rec') then}
  373. {    writeln('<', line);}
  374.                             HandleLine(line);
  375.                         end;
  376.                         if transfering then begin
  377.                             FailError(TBTransferTilDot(buffer, transfering_refnum, finished, 13));
  378.                             if finished then
  379.                                 HandleLine('');
  380.                         end;
  381.                         if error <> noErr then
  382.                             CloseConnection(connection);
  383.                         time_of_last_action := TickCount;
  384.                     end;
  385.                     C_Closing:  begin
  386.                         CloseConnection(connection);
  387.                     end;
  388.                     C_Closed:  begin
  389.                         quitNow := true;
  390.                     end;
  391.                 end;
  392.             end;
  393.         end;
  394.     end;
  395.  
  396.     var
  397.         app_resfile: integer;
  398.         app_fs: FSSpec;
  399.  
  400.     procedure GetAppInfo;
  401.         var
  402.             pb: FCBPBRec;
  403.             oe: OSErr;
  404.     begin
  405.         app_resfile := CurResFile;
  406.         pb.ioNamePtr := @app_fs.name;
  407.         pb.ioVRefNum := 0;
  408.         pb.ioRefNum := app_resfile;
  409.         pb.ioFCBIndx := 0;
  410.         oe := PBGetFCBInfo(@pb, false);
  411.         app_fs.vRefNum := pb.ioFCBVRefNum;
  412.         app_fs.parID := pb.ioFCBParID;
  413.     end;
  414.  
  415.     function GetDemoFolder: OSErr;
  416.     begin
  417.         demo_vrn := app_fs.vRefNum;
  418.         GetDemoFolder := MakeDirectory(GetGlobalStr(demo_name_index), app_fs.parID, demo_dirID);
  419.     end;
  420.  
  421.     var
  422.         dataresfile: integer;
  423.         host: str255;
  424.         cp: connectionIndex;
  425. begin
  426.     quitNow := false;
  427.     state := S_None;
  428.     transfering := false;
  429.     GetAppInfo;
  430.     CreateHandleFile(list_hf, CL_LF);
  431.     if NoError(GetDemoFolder) then begin
  432.         if NoError(InitConnections) then begin
  433.             dataresfile := HOpenResFile(app_fs.vRefNum, app_fs.parID, 'FetchNews Data', fsRdPerm); {GetGlobalStr(data_filename_index)}
  434.             if dataresfile = -1 then begin
  435.                 FailError(-1);
  436.             end
  437.             else begin
  438.                 InitCursor;
  439.                 host := GetIndStr(data_strh_id, host_index);
  440.                 quitNow := not NoError(FindAddress(cp, host, nil));
  441.  
  442.                 while not quitNow do begin
  443.                     WNE;
  444.                     HCE;
  445.                 end;
  446.  
  447.             end;
  448.             FinishEverything;
  449.         end;
  450.     end;
  451.     DestroyHandleFile(list_hf);
  452. {    writeln(error);}
  453. end.